﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using HeapSpecSupport;
using SimpleIR;

namespace Evaluator.EvalState
{
	/// <summary>
	/// This is the static code that handles the evaluation of a program.
	/// </summary>
	public static class Evaluator
	{
		///////////////////////////////
		//Helpers for insturmenting the programs execution.

		/// <summary>
		/// The current step number for the program execution.
		/// </summary>
		private static ulong s_executionTime;

		/// <summary>
		/// The current break at line.
		/// </summary>
		private static long s_breakline;

		/// <summary>
		/// Reset the evaluator state.
		/// </summary>
		public static void ResetEvaluator()
		{
			s_executionTime = 0;
			s_breakline = -1;
		}

		/// <summary>
		/// Get the current execution time.
		/// </summary>
		public static ulong CurrentExecutionTime
		{ get { return s_executionTime; } }

		private static void IncrementCurrentExecutionTime()
		{ s_executionTime++; }


		//////////////////////////////////////////
		//Manage debug state

#if DEBUG_ENABLED
		private static bool PreBreakTest(int line, LocalFrame lf)
		{ return (lf.DebuggerState >= LocalFrame.DebugState.DebugStep) || ((lf.DebuggerState == LocalFrame.DebugState.DebugBreakAt) && line == s_breakline); }
#else
		private static bool PreBreakTest(int line, MethodContextState mcs)
		{ return false; }
#endif

		private static void ProcessDebugCommandsStmt(IRByteCode bc, GlobalState ps)
		{
			Contract.Requires(bc != null);
			Contract.Requires(ps != null);

			String cmd;
			String sstr = bc.ToString();

			while(true)
			{
				Console.Write("\nstmt: " + bc.UseModLoc + ", " + sstr + "\n" + "> ");
				cmd = Console.ReadLine();

				if(cmd.Equals("r") || cmd.Equals("run"))
				{
					ps.CurrentCallFrame.DebuggerState = LocalFrame.DebugState.DebugBreakAt;
					break;
				}
				else if(cmd.StartsWith("b ") || cmd.StartsWith("break "))
				{
					int bl = cmd.IndexOf(' ');
					String target = cmd.Substring(bl + 1);
					s_breakline = Int32.Parse(target);
				}
				else if(cmd.Equals("d") || cmd.Equals("display"))
				{
					Console.Write("Not to useful yet!!!\n");
				}
				else if(cmd.Equals("si") || cmd.Equals("stepinto"))
				{
					ps.CurrentCallFrame.DebuggerState = LocalFrame.DebugState.DebugStepInto;
					break;
				}
				else if(cmd.Equals("s") || cmd.Equals("step"))
				{
					ps.CurrentCallFrame.DebuggerState = LocalFrame.DebugState.DebugStep;
					break;
				}
				else if(cmd.Equals("p") || cmd.Equals("print"))
				{
					String fgdgml = ExeProgram.TState.CurrentCallFrame.CurrentMethod.Body.DGMLDisplayString();
					System.IO.File.WriteAllText(System.IO.Path.Combine(ExeProgram.GlobalDataDir, "flowgraph.dgml"), fgdgml);
				}
				else if(cmd.Equals("cg") || cmd.Equals("callgraph"))
				{
					System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, CloseOutput = true, Encoding = Encoding.UTF8 };

					try
					{
						String ffn = System.IO.Path.GetFullPath(ExeProgram.GlobalDataDir + "partialcg.dgml");
						if(System.IO.File.Exists(ffn))
							System.IO.File.Delete(ffn);

						using(System.Xml.XmlWriter cgwriter = System.Xml.XmlTextWriter.Create(ffn, settings))
						{
							ExeProgram.TProg.CallGraph.SerializeToDGML(cgwriter, ExeProgram.TProg);
						}
					}
					catch(System.IO.IOException)
					{
						Console.WriteLine("Unable to write call graph!!");
					}
				}
				else
				{
					Console.WriteLine("Unknown Command");
					Console.WriteLine("run [r]: Analyze till breakpoint.");
					Console.WriteLine("break [b]: Set a breakpoint at the line/function.");
					Console.WriteLine("display [d] exp: Display the current value for the expression.");
					Console.WriteLine("stepinto [si]: Analyze the stmt, going into call if needed.");
					Console.WriteLine("step [s]: Analyze the stmt, do not go into expressions.");
					Console.WriteLine("print [p]: Display flow graph for the current method.");
					Console.WriteLine("callgraph [cg]: Display the call graph for the program.");
				}
			}
		}

		//////////////////////////////////////////
		//Manage the execution of a single opcode

		/// <summary>
		/// Analyze a single stmt.
		/// </summary>
		private static void AnalyzeStmt(IRByteCode bc, GlobalState pstate)
		{
			Contract.Requires(bc != null);
			Contract.Requires(pstate != null);

			if(PreBreakTest(bc.UseModLoc, pstate.CurrentCallFrame))
			{
				Console.WriteLine("\nBefore Stmt Analysis");
				ProcessDebugCommandsStmt(bc, pstate);
			}

			++s_executionTime;

			switch(bc.OperationCode)
			{
				case OpCode.Op.BranchU:
					pstate.CurrentCallFrame.JumpToNewBlock(((IRBranchOp)bc).BRUTarget);
					break;
				case OpCode.Op.BranchC:
					Evaluator.AnalyzeBranchCond((IRBranchOp)bc, pstate);
					break;
				case OpCode.Op.Switch:
					Evaluator.AnalyzeSwitch((IRBranchOp)bc, pstate);
					break;
				case OpCode.Op.Throw:
					Console.WriteLine("Throwing runtime user exception -- terminating");
					System.Environment.Exit(1);
					break;
				case OpCode.Op.Return:
					Evaluator.AnalyzeReturn((IRBranchOp)bc, pstate);
					return;
				case OpCode.Op.Call:
					if(((IRCallOp)bc).InvokeSig.IsVirtualInvoke)
						Evaluator.AnalyzeVirtualCall((IRCallOp)bc, pstate);
					else
						Evaluator.AnalyzeStaticCall((IRCallOp)bc, pstate);
					return;
				case OpCode.Op.CallVirt:
					if(((IRCallOp)bc).ReciverExpType.DerefTargetType is IRLambdaType && ((IRCallOp)bc).InvokeSig.InvokeName.Equals("Invoke"))
						Evaluator.AnalyzeLambdaInvoke((IRCallOp)bc, pstate);
					else
						Evaluator.AnalyzeVirtualCall((IRCallOp)bc, pstate);
					return;
				case OpCode.Op.AllocCons:
					Evaluator.AnalyzeAllocObj((IRAllocationOp)bc, pstate);
					return;
				case OpCode.Op.LoadPrimFromLocal:
					pstate.CurrentCallFrame.SimulateLoadNumericFromLocal(((IRLoadToEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadPrimFromGlobal:
					pstate.SimulateLoadPrimFromGlobal(((IRLoadToEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StorePrimToLocal:
					pstate.CurrentCallFrame.SimulateStoreNumericToLocal(((IRStoreFromEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StorePrimToGlobal:
					pstate.SimulateStorePrimToGlobal(((IRStoreFromEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.PrimEQ:
					pstate.CurrentCallFrame.SimulatePrimEQ();
					break;
				case OpCode.Op.Less:
					pstate.CurrentCallFrame.SimulateLess();
					break;
				case OpCode.Op.LessEQ:
					pstate.CurrentCallFrame.SimulateLessEQ();
					break;
				case OpCode.Op.Greater:
					pstate.CurrentCallFrame.SimulateGreater();
					break;
				case OpCode.Op.GreaterEQ:
					pstate.CurrentCallFrame.SimulateGreaterEQ();
					break;
				case OpCode.Op.Add:
					pstate.CurrentCallFrame.SimulateAdd();
					break;
				case OpCode.Op.Div:
					pstate.CurrentCallFrame.SimulateDiv();
					break;
				case OpCode.Op.Mult:
					pstate.CurrentCallFrame.SimulateMult();
					break;
				case OpCode.Op.Neg:
					pstate.CurrentCallFrame.SimulateNeg();
					break;
				case OpCode.Op.Rem:
					pstate.CurrentCallFrame.SimulateRem();
					break;
				case OpCode.Op.Sub:
					pstate.CurrentCallFrame.SimulateSub();
					break;
				case OpCode.Op.And:
					pstate.CurrentCallFrame.SimulateAnd();
					break;
				case OpCode.Op.Or:
					pstate.CurrentCallFrame.SimulateOr();
					break;
				case OpCode.Op.Not:
					pstate.CurrentCallFrame.SimulateNot();
					break;
				case OpCode.Op.Shl:
					pstate.CurrentCallFrame.SimulateShl();
					break;
				case OpCode.Op.Shr:
					pstate.CurrentCallFrame.SimulateShr();
					break;
				case OpCode.Op.Xor:
					pstate.CurrentCallFrame.SimulateXor();
					break;
				case OpCode.Op.Conv:
					pstate.CurrentCallFrame.SimulateConvOp((IRPrimitiveType)((IREvalStackTypeOp)bc).TypeArg, ((IREvalStackTypeOp)bc).ConvSPos);
					break;
				case OpCode.Op.LoadStructOrClassFromLocal:
					pstate.CurrentCallFrame.SimulateLoadStructOrClassFromLocalToStack(((IRLoadToEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StoreStructOrClassToLocal:
					pstate.CurrentCallFrame.SimulateStoreStructOrClassFromStackToLocal(((IRStoreFromEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadStructOrClassFromGlobal:
					pstate.SimulateLoadStructOrClassFromGlobalToStack(((IRLoadToEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StoreStructOrClassToGlobal:
					pstate.SimulateStoreStructOrClassFromStackToGlobal(((IRStoreFromEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadReferenceFromLocal:
					pstate.CurrentCallFrame.SimulateLoadDeRefableFromLocalToStack(((IRLoadToEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StoreReferenceToLocal:
					pstate.CurrentCallFrame.SimulateStoreDeRefableFromStackToLocal(((IRStoreFromEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadPointerFromLocal:
					pstate.CurrentCallFrame.SimulateLoadDeRefableFromLocalToStack(((IRLoadToEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StorePointerToLocal:
					pstate.CurrentCallFrame.SimulateStoreDeRefableFromStackToLocal(((IRStoreFromEvalStackBasic)bc).LocalVar, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadPointerFromGlobal:
					pstate.SimulateLoadDeRefableFromGlobalToStack(((IRLoadToEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.StorePointerToGlobal:
					pstate.SimulateStoreDeRefableFromStackToGlobal(((IRStoreFromEvalStackBasic)bc).StaticField, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectReadOfPrimitive:
					pstate.CurrentCallFrame.SimulateDeRefIndirectReadOfNumeric(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectReadOfStructOrClass:
					pstate.CurrentCallFrame.SimulateDeRefIndirectReadOfStructOrClass(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectReadOfPointer:
					pstate.CurrentCallFrame.SimulateDeRefIndirectReadOfPointer(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectWritePrimitive:
					pstate.CurrentCallFrame.SimulateDeRefIndirectWritePrimitive(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectWriteStructOrClass:
					pstate.CurrentCallFrame.SimulateDeRefIndirectWriteStructOrClass(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectWritePointer:
					pstate.CurrentCallFrame.SimulateDeRefIndirectWritePointer(s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IndirectInitializeMem:
					pstate.CurrentCallFrame.SimulateDeRefIndirectInitializeOfStructOrClass(((IRStoreFromEvalStackIndirect)bc).ThroughRefType.DerefTargetType, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.LoadLocalAddr:
					pstate.CurrentCallFrame.SimulateLoadLocalAddr(((IRPointerIndexOp)bc).LocalName);
					break;
				case OpCode.Op.LoadGlobalAddr:
					pstate.SimulateLoadGlobalAddr(((IRLoadToEvalStackBasic)bc).StaticField);
					break;
				case OpCode.Op.IndexRefAddrByField:
					pstate.CurrentCallFrame.SimulateIndexAddrByFieldInclContainer(((IRPointerIndexOp)bc).IndexField, -1, ((IRPointerIndexOp)bc).StackIndexOfDeRefable);
					break;
				case OpCode.Op.IndexRefAddrByArrayIndex:
					int cindex = (int)((ESignIntegral)pstate.CurrentCallFrame.EvalStackPopWVal()).SignIntegralValue;
					IRMemberField arrayf = ((IRPointerIndexOp)bc).ArrayType.TypeDescription.LookupFieldInClass("allf");
					pstate.CurrentCallFrame.SimulateIndexAddrByFieldInclContainer(arrayf, cindex, ((IRPointerIndexOp)bc).StackIndexOfDeRefable - 1);
					break;
				case OpCode.Op.LoadFuncPtr:
					pstate.CurrentCallFrame.SimulateLoadFuncPtr(((IRLoadToEvalStackBasic)bc).InvokeIdentity);
					break;
				case OpCode.Op.LoadVFuncPtr:
					DebugReport.ReportError("Load VFunc pointer Not Supported Yet.");
					break;
				case OpCode.Op.DerefableEQ:
					pstate.CurrentCallFrame.SimulateDrefableEQ();
					break;
				case OpCode.Op.NonNull:
					pstate.CurrentCallFrame.SimulatePointerNonNull();
					break;
				case OpCode.Op.Box:
					pstate.CurrentCallFrame.SimulateBox((IRValueType)((IREvalStackTypeOp)bc).TypeArg, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.UnBox:
					pstate.CurrentCallFrame.SimulateUnBox((IRValueType)((IREvalStackTypeOp)bc).TypeArg, -1, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.Cast:
					pstate.CurrentCallFrame.SimulateCast((IRDerefableType)((IREvalStackTypeOp)bc).TypeArg, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.IsInst:
					pstate.CurrentCallFrame.SimulateIsInst((IRDerefableType)((IREvalStackTypeOp)bc).TypeArg, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.ConvToRef:
					pstate.CurrentCallFrame.SimulateConvToReference(((IREvalStackTypeOp)bc).ConvSPos, (IRReferenceType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.ConvToPtr:
					pstate.CurrentCallFrame.SimulateConvToPointer(((IREvalStackTypeOp)bc).ConvSPos, (IRPtrType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.ConstrainRcvr:
					pstate.CurrentCallFrame.SimulateConstrain(((IREvalStackTypeOp)bc).ConvSPos, (IRReferenceType)((IREvalStackTypeOp)bc).TypeArg, s_executionTime, bc.UseModLoc);
					break;
				case OpCode.Op.Pop:
					pstate.CurrentCallFrame.SimulatePop();
					break;
				case OpCode.Op.Dup:
					pstate.CurrentCallFrame.SimulateDup();
					break;
				case OpCode.Op.PushNull:
					pstate.CurrentCallFrame.SimulatePushNull();
					break;
				case OpCode.Op.PushInt:
					pstate.CurrentCallFrame.SimulatePushInt(((IRLoadToEvalStackBasic)bc).Literal.IntVal);
					break;
				case OpCode.Op.PushUInt:
					pstate.CurrentCallFrame.SimulatePushUInt(((IRLoadToEvalStackBasic)bc).Literal.UIntVal);
					break;
				case OpCode.Op.PushFloat:
					pstate.CurrentCallFrame.SimulatePushFloat(((IRLoadToEvalStackBasic)bc).Literal.FloatVal);
					break;
				case OpCode.Op.PushString:
					pstate.CurrentCallFrame.SimulatePushString(s_executionTime, bc.UseModLoc, ((IRLoadToEvalStackBasic)bc).Literal.StringVal);
					break;
				default:
					DebugReport.ReportError("Bad Op in analyze switch.");
					break;
			}
		}

		private static void AnalyzeBranchCond(IRBranchOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null);
			Contract.Requires(pstate != null);

			pstate.CurrentCallFrame.SimulateConvOp(ExeProgram.TProg.TypeTable.BoolType, 0);
			ESignIntegral bval = (ESignIntegral)pstate.CurrentCallFrame.EvalStackPopWVal();

			if(bval.SignIntegralValue == 0)
				pstate.CurrentCallFrame.JumpToNewBlock(bc.BRCondFalseTarget);
			else
				pstate.CurrentCallFrame.JumpToNewBlock(bc.BRCondTrueTarget);
		}

		private static void AnalyzeSwitch(IRBranchOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null);
			Contract.Requires(pstate != null);

			//this is strange so lets break and figure it out
			if(System.Diagnostics.Debugger.IsAttached)
				System.Diagnostics.Debugger.Break();
			System.Diagnostics.Debugger.Launch();

			EUSignIntegral bval = (EUSignIntegral)pstate.CurrentCallFrame.EvalStackPopWVal();

			//if default case take last target
			if(bval.USignIntegralValue >= (ulong)bc.SwitchTargets.Length)
				pstate.CurrentCallFrame.JumpToNewBlock(bc.SwitchTargets[bc.SwitchTargets.Length - 1]);
			else
				pstate.CurrentCallFrame.JumpToNewBlock(bc.SwitchTargets[bval.USignIntegralValue]);
		}

		private static void AnalyzeVirtualCall(IRCallOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null, "for pure virtual");
			Contract.Requires(pstate != null);

			Evaluator.Display__CallToSig(bc.InvokeSig);

			pstate.CurrentCallFrame.IncrementPC();

			List<IRType> argtl = bc.InvokeSig.InvokeSig;
			int stackstart = pstate.CurrentCallFrame.GetFirstLocalStackPosForCall(argtl.Count + 1);
			
			IRType cdi = pstate.CurrentCallFrame.GetTargetCallClassForName(stackstart, (IRVirtualInvoke)bc.InvokeSig);

			//get the type that declares the virtual method target we want and mangle any implicit unboxing or pointer promotion
			IRType btti;
			if(cdi is IRBoxedType)
			{
				btti = ((IRBoxedType)cdi).BoxedTypeValue;
				pstate.CurrentCallFrame.SimulateUnBox((IRValueType)btti, stackstart, s_executionTime, bc.UseModLoc);
			}
			else
			{
				btti = cdi;
				pstate.CurrentCallFrame.SetArgType(ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(cdi), stackstart);
			}

			IRType baselookuptype = (bc.HasSpecificTarget ? bc.SpecificTargetClass : btti);
			IRType actualImplClass = IRProgram.GetImplClassWithThisAsBaseType(baselookuptype, (IRVirtualInvoke)bc.InvokeSig);
			IRBodyDef bd = ExeProgram.TProg.GetBodyForClassAndSig(actualImplClass, bc.InvokeSig);

			pstate.CurrentCallFrame.SetArgType(ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(actualImplClass), stackstart);

			for(int j = 0; j < argtl.Count; ++j)
			{
				IRType tt = argtl[j];
				pstate.CurrentCallFrame.SetArgType(tt, stackstart + 1 + j);
			}

			if(bd.IsBuiltin)
				pstate.SimulateBuiltinCall(bd, false, s_executionTime, bc.UseModLoc);
			else
				pstate.EnterMethodCall(bd, false, s_executionTime);
		}

		private static void AnalyzeLambdaInvoke(IRCallOp bc, GlobalState pstate)
		{
			throw new NotImplementedException();
		}

		private static void AnalyzeStaticCall(IRCallOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null && (bc.InvokeSig.IsStaticInvoke));
			Contract.Requires(pstate != null);

			Evaluator.Display__CallToSig(bc.InvokeSig);

			IRBodyDef bd = ExeProgram.TProg.GetBodyForClassAndSig(bc.SpecificTargetClass, bc.InvokeSig);
			List<IRType> argtl = bc.InvokeSig.InvokeSig;
			int stackstart = pstate.CurrentCallFrame.GetFirstLocalStackPosForCall(argtl.Count);

			for(int j = 0; j < argtl.Count; ++j)
			{
				IRType tt = argtl[j];
				pstate.CurrentCallFrame.SetArgType(tt, stackstart + j);
			}

			if(bd.IsBuiltin)
				pstate.SimulateBuiltinCall(bd, false, s_executionTime, bc.UseModLoc);
			else
				pstate.EnterMethodCall(bd, false, s_executionTime);
		}

		private static void AnalyzeAllocObj(IRAllocationOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null);
			Contract.Requires(pstate != null);

			IRReferenceType allocreftype = ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(bc.AllocType);
			IRBodyDef bd = ExeProgram.TProg.GetBodyForClassAndSig(bc.AllocType, bc.ConsIdentity.Sig);
			List<IRType> argtl = bc.ConsIdentity.Sig.InvokeSig;

			if(bc.IsArrayAlloc)
			{
				IRPtrType drftype = ExeProgram.TProg.TypeTable.LookupPointerTypeFor((IRContainerType)bc.AllocType);
				ESignIntegral asize = (ESignIntegral)pstate.CurrentCallFrame.EvalStackPopWVal();
				EArrayObject aobj = EArrayObject.CreateFreshArrayValueForType((IRContainerType)bc.AllocType, (int)asize.SignIntegralValue);
				ELocation nloc = ELocation.AllocateHeapMemory(bc.AllocType, s_executionTime, bc.UseModLoc);
				nloc.StoreLocation_Direct(aobj, s_executionTime, bc.UseModLoc);

				EPointer pv = new EPointer(drftype, nloc);
				pstate.CurrentCallFrame.EvalStackPush(pv);
			}
			else
			{
				pstate.CurrentCallFrame.SimulateAllocBlank(bc.AllocType, s_executionTime, bc.UseModLoc);

				if(!(bc.AllocType is IRStructType))
					pstate.CurrentCallFrame.SimulateConvToReference(0, allocreftype);

				int stackstart = pstate.CurrentCallFrame.GetFirstLocalStackPosForCall(argtl.Count + 1);
				pstate.CurrentCallFrame.RotateBlankAllocToRecvrPosition(stackstart);

				Evaluator.Display__CallToSig(bc.ConsIdentity.Sig);

				for(int j = 0; j < argtl.Count; ++j)
				{
					IRType tt = argtl[j];
					pstate.CurrentCallFrame.SetArgType(tt, stackstart + 1 + j);
				}

				if(bd.IsBuiltin)
					pstate.SimulateBuiltinCall(bd, true, s_executionTime, bc.UseModLoc);
				else
					pstate.EnterMethodCall(bd, false, s_executionTime);
			}
		}

		private static void AnalyzeReturn(IRBranchOp bc, GlobalState pstate)
		{
			Contract.Requires(bc != null);
			Contract.Requires(pstate != null);

			pstate.ExitMethodCall();
		}

		//////////////////////////////////////////////////
		//Diagnoistics 

		//NOISY, may want to comment the logging out to reduce noise later
		[System.Diagnostics.Conditional("DEBUG_ENABLED")]
		internal static void Display__CallToSig(IRInvokeSig sig)
		{
			Console.WriteLine("    Call to Method with Sig: " + sig.ToString() + "."); 
		}
	}

	public static class DebugReport
	{
		//Debug and other info that we would like to access from a number of places
		private static long currentStmt = 0;
		private static ulong currentTime = 0;
		private static String currentInvoke = "notRunning";

		public static void SetCurrentStmtDBG(long stmt)
		{ currentStmt = stmt; }

		public static void SetCurrentTimeDBG(ulong time)
		{ currentTime = time; }

		public static void SetCurrentInvokeDBG(String invokeStr)
		{ currentInvoke = invokeStr; }

		public static void ReportError(String msg)
		{
#if DEBUG
			throw new DebugException(msg, currentInvoke, currentStmt, currentTime);
#else
			Console.WriteLine("Term Abnormal with Message: " + msg);
			System.Environment.Exit(1);	
#endif
		}

		public static void ReportError()
		{
#if DEBUG
			throw new DebugException("", currentInvoke, currentStmt, currentTime);
#else
			Console.WriteLine("Term Abnormal");
			System.Environment.Exit(1);	
#endif
		}
	}

	[Serializable]
	public sealed class DebugException : Exception
	{
		public DebugException(String msg, String cinv, long cstmt, ulong ctime)
			: base("In " + cinv + " at " + cstmt + " time=" + ctime + ": " + msg)
		{ ;}
	}
}